#define _GNU_SOURCE
#include "addrspace.h"
#include "userthread.h"
#include "function.h"
#include "system.h"
#include "thread.h"

using namespace std;


static void StartUserThread(void* function) {
  DEBUG('p', "\tuserthread.cc: StartUserThread\n");
  Function *func = (Function*) function;
  // initialize registers
  
  int i;

  for (i = 0; i < NumTotalRegs; i++)
     machine->WriteRegister (i, 0);

  machine-> WriteRegister(4, func-> arg);

  // Initial program counter -- must be location of "Start"*
  machine->WriteRegister (PCReg, func-> f);

  // Need to also tell MIPS where next instruction is, because
  // of branch delay possibility
  machine->WriteRegister (NextPCReg, machine->ReadRegister(PCReg) + 4);

  // Set the stack register to the end of the address space, where we
  // allocated the stack
  machine->WriteRegister (StackReg, func-> addr);
  DEBUG('p', "\tuserthread.cc: PCReg: %d, NextPCReg: %d, StackReg: %d\
             func->arg: %d, func->addr: %d, func->f: %d\n",
             machine->ReadRegister(PCReg), machine->ReadRegister(NextPCReg),
             machine->ReadRegister(StackReg), func->arg, func->addr, func->f);

  machine-> Run();
}

int UserThread::do_ThreadCreate(int f, int arg) {
  DEBUG ('p', "\tuserthread.cc: do_ThreadCreate: f = %d, arg = %d\n", f, arg);
  char* str;
  asprintf(&str, "f = %d, arg = %d", f, arg);
  Thread *th = new Thread(str);
  int addr;

  while((addr = currentThread-> space-> AllocateUserStack()) == -1)
    currentThread->Yield();

  Function *func = new Function(f, arg, addr);
  th-> Start(StartUserThread, func);

  return 0;
}

int UserThread::do_ThreadExit() {
  DEBUG('p', "\tuserthread.cc: do_ThreadExit: StackReg: %d\n", machine->ReadRegister(StackReg) + 24);
  currentThread->space->DeallocateUserStack(machine->ReadRegister(StackReg) + 24);
  bool finished = currentThread->space->slotsFree();

  if (finished) {
    machine-> numProcesses--;
    if(machine -> numProcesses == 0)
      interrupt->Halt();
  }

  currentThread-> Finish();
  return 0;
}